
BeeNET 於 v3.4.0 在資料存取層進行了大幅重構,過去的
DbCommandHelper與SysDb等輔助類別,現在都統整為單一的DbAccess類別。
開發者不再需要手動管理DbConnection、DbCommand與DbDataReader,即可輕鬆完成資料操作。此外,DbAccess也支援批次命令與交易,以及外部連線的整合,讓使用更安全、彈性。本文整理了改版重點與實際使用方式。
DbAccess:所有資料存取皆透過 DbAccess,降低耦合度並減少學習成本。DbConnection、DbCommand 與 DbDataReader。ExecuteBatch* 可一鍵包交易;任一命令失敗即回滾,並拋出包含失敗索引的例外。Execute(spec, DbTransaction) / ExecuteAsync(spec, DbTransaction) 參與外部交易。DbAccess API 概觀// 內部連線模式:以 databaseId 解析 Provider 與 ConnectionString
var db1 = new DbAccess("HR");
// 外部連線模式:連線與交易生命週期由外部管理
using (var conn = new SqlConnection(connString))
{
    conn.Open();
    var db2 = new DbAccess(conn); // 外部連線型態與 Provider 對應由框架管理
}
// 同步
DbCommandResult result = db1.Execute(spec);
// 非同步
DbCommandResult resultAsync = await db1.ExecuteAsync(spec, cancellationToken);
var batch = new DbBatchSpec
{
    UseTransaction = true,
    IsolationLevel = IsolationLevel.ReadCommitted, // 可選
    Commands =
    {
        new DbCommandSpec(DbCommandKind.NonQuery, "INSERT INTO Department(Name) VALUES({0})", "R&D"),
        new DbCommandSpec(DbCommandKind.NonQuery, "INSERT INTO Employee(Name, DepartmentId) VALUES({0}, {1})", "Alice", 5)
    }
};
// 同步
DbBatchResult batchResult = db1.ExecuteBatch(batch);
// 非同步
DbBatchResult batchResultAsync = await db1.ExecuteBatchAsync(batch, cancellationToken);
若批次第
i筆失敗:框架會嘗試回滾並拋出
InvalidOperationException($"Failed to execute batch: Command at index {i} failed.", innerException),便於快速定位。
using (var conn = new SqlConnection(connString))
{
    conn.Open();
    using (var tran = conn.BeginTransaction())
    {
        var db = new DbAccess(conn);
        db.Execute(spec1, tran);                   // 同步
        await db.ExecuteAsync(spec2, tran, token); // 非同步
        tran.Commit();
    }
}
⚠️ 注意:若使用外部交易 (
DbTransaction),請僅呼叫Execute/ExecuteAsync,不應再呼叫ExecuteBatch。
// Query:直接回傳 List<T>
var list = db1.Query<Employee>(spec);
// QueryAsync:直接回傳 List<T>
var listAsync = await db1.QueryAsync<Employee>(spec, cancellationToken);
var spec = new DataTableUpdateSpec
{
    DataTable = table,
    InsertCommand = insertSpec,
    UpdateCommand = updateSpec,
    DeleteCommand = deleteSpec,
    UseTransaction = true,
    IsolationLevel = IsolationLevel.ReadCommitted // 可選
};
var db = new DbAccess("HR");
int affected = db.UpdateDataTable(spec);
DbCommandSpec 說明DbCommandSpec 是資料庫命令的中介類別:你只需描述「要做什麼」(SQL + 參數 + 命令種類),它就會在執行前依照 DatabaseType 自動轉換成正確的 DbCommand 與 DbParameter。
兩種參數模式
{0}, {1}, …(短小、直覺){name}, {hiredOn}, …(欄位多時更不易放錯)依 DatabaseType 套用參數前綴
你在 SQL 中寫 {name} 或 {0},實際執行時會自動轉換為正確的參數名稱與 DbParameter。
@name
:name
?name
Stored Procedure 直接傳遞
當 CommandType = StoredProcedure 時,CommandText 不做參數占位符解析,直接用 SP 名稱與參數集合建立命令。
Timeout 規則
CommandTimeout <= 0 以預設 30 秒;若超過全域上限則自動套用上限。
安全性
不論位置或具名參數,都不會做字串拼接;在 CreateCommand() 期間會轉成 DbParameter,可有效防止 SQL Injection。
DbCommandSpec 使用範例string sql = @"
SELECT  EmployeeId,
        Name,
        HiredOn
FROM    Employee
WHERE   EmployeeId = {0}";
var command = new DbCommandSpec(DbCommandKind.DataTable, sql, 1001);
var db = new DbAccess("HR");
var employees = db.Query<Employee>(command); // List<Employee>
var spec = new DbCommandSpec(
    DbCommandKind.Scalar,
    "SELECT COUNT(*) FROM Employee WHERE DepartmentId = {0}",
    5
);
var db = new DbAccess("HR");
var result = db.Execute(spec);
int count = Convert.ToInt32(result.Scalar);
var spec = new DbCommandSpec(
    DbCommandKind.NonQuery,
    "INSERT INTO Department(Name, CreatedOn, IsActive) VALUES({0}, {1}, {2})",
    "R&D", DateTime.Now, true
)
{
    CommandTimeout = 60 // <=0 使用預設 30 秒;超過全域上限則套用上限(由 setter 處理)
};
var db = new DbAccess("HR");
DbCommandResult r = db.Execute(spec);
Console.WriteLine($"Rows affected: {r.RowsAffected}");
var spec = new DbCommandSpec(
    DbCommandKind.NonQuery,
    "UPDATE Employee SET Name = {name}, HiredOn = {hiredOn} WHERE EmployeeId = {employeeId}",
    new Dictionary<string, object>
    {
        ["name"] = "Alice",
        ["hiredOn"] = new DateTime(2024, 1, 1),
        ["employeeId"] = 1001
    }
);
var db = new DbAccess("HR");
db.Execute(spec);
var spec = new DbCommandSpec(
    DbCommandKind.NonQuery,
    "usp_UpdateSalary",
    new Dictionary<string, object>
    {
        ["employeeId"] = 1001,
        ["delta"] = 1000m
    }
)
{
    CommandType = CommandType.StoredProcedure
};
var db = new DbAccess("HR");
db.Execute(spec);
透過這次的改版,DbAccess 已成為 BeeNET 中唯一且安全的資料存取入口:
開發者現在可以更專注於 SQL 與業務邏輯本身,讓資料存取更直觀、更可靠。
📘 HackMD 原文筆記:
👉 https://hackmd.io/@jeff377/dbaccess-v2
📢 歡迎轉載,請註明出處
📬 歡迎追蹤我的技術筆記與實戰經驗分享
Facebook | HackMD | GitHub | NuGet